package com.jxtc.enterprise.delivery.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jxtc.enterprise.common.constants.OrderPaymentStatus;
import com.jxtc.enterprise.common.entity.DeliveryLocker;
import com.jxtc.enterprise.common.entity.OrderDetail;
import com.jxtc.enterprise.common.entity.OrderPayment;
import com.jxtc.enterprise.common.mapper.DeliverLockerMapper;
import com.jxtc.enterprise.common.mapper.OrderDetailMapper;
import com.jxtc.enterprise.common.mapper.OrderMapper;
import com.jxtc.enterprise.common.service.DeliveryLockerOpenApiService;
import com.jxtc.enterprise.common.utils.Result;
import com.jxtc.enterprise.common.utils.ResultGenerator;
import com.jxtc.enterprise.common.vo.DeliverBoxApiParamVO;
import com.jxtc.enterprise.common.vo.DeliveryLockerOpenDoorParameterVO;
import com.jxtc.enterprise.common.vo.DeliveryLockerOpenDoorVO;
import com.jxtc.enterprise.delivery.vo.deliveryLocker.req.DeliveryLockerBoxForStoreReqVo;
import com.jxtc.enterprise.delivery.vo.deliveryLocker.resp.DeliveryLockerCabAndBoxInfoRespVo;
import com.jxtc.enterprise.delivery.vo.deliveryLocker.resp.DeliveryLockerCabRespVo;
import com.jxtc.enterprise.tenant.vo.orderMange.resp.WaitIntervalForBatchOpenMultiDoorRespVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/jxtc/enterprise/delivery/deliveryLockerManage")
@RequiredArgsConstructor
@Slf4j
public class RiderDeliveryLockerManageController {

    private final OrderMapper orderMapper;
    private final OrderDetailMapper orderDetailMapper;
    private final DeliverLockerMapper deliverLockerMapper;

    private final DeliveryLockerOpenApiService deliveryLockerOpenApiService;

    /**
     * 当一次打开一个柜门时，调用开柜门接口后，等待的间隔时间列表，以 "," 分割的字符串
     */
    @Value("${locker.waitIntervalInMsList}")
    private String waitIntervalInMsList = "500,1000,3000,5000";

    /**
     * 默认选中等待的间隔时间，的配合 waitIntervalInMsList 一起使用，必选是 waitIntervalInMsList 中的某一项
     */
    @Value("${locker.defaultSelectedWaitIntervalInMs}")
    private Integer defaultSelectedWaitIntervalInMs = 3000;

    /**
     * 6.6.1 根据订单详情 ID 打开指定外卖柜
     *
     * @param orderDetailId 订单详情 ID
     */
    @GetMapping("/openDeliveryLockerByOrderDetailId")
    public Result<Void> openDeliveryLockerByOrderDetailId(@RequestParam(value = "orderDetailId") String orderDetailId) {
        log.info("/openDeliveryLockerByOrderDetailId, orderDetailId: {}", orderDetailId);
        OrderDetail orderDetail = orderDetailMapper.selectById(orderDetailId);
        if (orderDetail == null) {
            log.warn("/openDeliveryLockerByOrderDetailId, 未找到对应的订单详情, orderDetailId: {}", orderDetailId);
            return ResultGenerator.genFailResult("ID 为 " + orderDetailId + " 的订单详情不存在");
        }

        if (!StringUtils.hasText(orderDetail.getLockerInfo())) {
            log.warn("/openDeliveryLockerByOrderDetailId, 该订单不存在外卖柜信息，无法打开柜门, orderDetailId: {}, lockerInfo: {}", orderDetailId, orderDetail.getLockerInfo());
            return ResultGenerator.genFailResult("该订单不存在外卖柜信息，无法打开柜门");
        }

        DeliverBoxApiParamVO boxApiParamVO = JSONObject.parseObject(orderDetail.getLockerInfo(), DeliverBoxApiParamVO.class);

        String clientNo = boxApiParamVO.getClientNo();
        // 这里直接使用订单详情 ID 作为 orderNo，每笔订单详情 ID 一定是不重复的
        String orderNo = orderDetail.getId();
        String appId = boxApiParamVO.getAppId();
        String appKey = boxApiParamVO.getAppKey();
        Integer cab = boxApiParamVO.getCab();
        Integer col = boxApiParamVO.getCol();

        DeliveryLockerOpenDoorParameterVO openDoorVo = new DeliveryLockerOpenDoorParameterVO();
        openDoorVo.setOrderNo(orderNo);
        openDoorVo.setCab(String.valueOf(cab));
        // 依次调用打开柜门的接口，所以每一打开一个柜门，sn 一直为 1
        openDoorVo.setSn("1");
        openDoorVo.setCol(String.valueOf(col));
        openDoorVo.setPrice("0.00");
        openDoorVo.setNo(orderDetail.getLockerBoxNo());
        openDoorVo.setName("");
        openDoorVo.setType("5");

        // 调用打开柜门接口
        deliveryLockerOpenApiService.openDoorWithoutSleep(clientNo, orderNo, appId, appKey, openDoorVo);
        return ResultGenerator.genSuccessResult();
    }


    /**
     * 6.6.2 打开某个公司所属的指定外卖柜
     *
     * @param deliveryLockerId 外卖柜 ID
     * @param col              货道号（窗口编号）
     */
    @GetMapping("/openOneDeliveryLockerForCompany")
    public Result<Void> openOneDeliveryLocker(@RequestParam(value = "deliveryLockerId") String deliveryLockerId,
                                              @RequestParam(value = "col") Integer col) {
        log.info("/openOneDeliveryLocker, deliveryLockerId: {}, col: {}", deliveryLockerId, col);
        DeliveryLocker deliveryLocker = deliverLockerMapper.selectById(deliveryLockerId);
        if (deliveryLocker == null) {
            log.warn("/openDeliveryLockerBoxByIdAndCol, 未找到对应的外卖柜, deliveryLockerId: {}", deliveryLockerId);
            return ResultGenerator.genFailResult("ID 为 " + deliveryLockerId + " 的外卖柜不存在");
        }

        String clientNo = deliveryLocker.getClientNo();
        // 使用 UUID 生成一个 32 位的字符串作为 orderNo
        String orderNo = UUID.randomUUID().toString().replace("-", "");
        String appId = deliveryLocker.getAppId();
        String appKey = deliveryLocker.getAppKey();
        Integer cab = deliveryLocker.getCab();

        DeliveryLockerOpenDoorParameterVO openDoorVo = new DeliveryLockerOpenDoorParameterVO();
        openDoorVo.setOrderNo(orderNo);
        openDoorVo.setCab(String.valueOf(cab));
        openDoorVo.setSn("1");
        openDoorVo.setCol(String.valueOf(col));
        openDoorVo.setPrice("0.00");
        openDoorVo.setNo(MessageFormat.format("{0}_{1}", deliveryLocker.getLockerLabel(), col));
        openDoorVo.setName("");
        openDoorVo.setType("5");

        deliveryLockerOpenApiService.openDoorWithoutSleep(clientNo, orderNo, appId, appKey, openDoorVo);
        return ResultGenerator.genSuccessResult();
    }


    /**
     * 6.6.3 打开某个公司在指定店铺下所有订单的柜门
     */
    @PostMapping("/openAllDeliveryLockerForOrder")
    public Result<Void> openAllDeliveryLockerForOrder(@Valid @RequestBody DeliveryLockerBoxForStoreReqVo reqVo) {
        log.info("/openAllDeliveryLockerForOrder, request: {}", JSONObject.toJSONString(reqVo));
        LambdaQueryWrapper<OrderPayment> orderPaymentWrapper = new LambdaQueryWrapper<OrderPayment>()
                .eq(OrderPayment::getCompanyId, reqVo.getCompanyId())
                .eq(OrderPayment::getDateFlag, reqVo.getDateFlag())
                // 仅查询订单状态为 2, 3, 8 的订单
                .in(OrderPayment::getStatus, OrderPaymentStatus.TO_SEND.getValue(), OrderPaymentStatus.TO_RECEIVE.getValue(), OrderPaymentStatus.COMPLETED.getValue())
                .select(OrderPayment::getId);
        List<String> orderIdList = orderMapper.selectObjs(orderPaymentWrapper);
        if (CollectionUtils.isEmpty(orderIdList)) {
            log.warn("/openAllDeliveryLockerForOrder, 没有符合条件的订单, companyId: {}, dateFlag: {}", reqVo.getCompanyId(), reqVo.getDateFlag());
            return ResultGenerator.genFailResult("打开柜门失败，该公司今日没有符合条件的订单");
        }

        LambdaQueryWrapper<OrderDetail> orderDetailQueryWrapper = new LambdaQueryWrapper<OrderDetail>()
                .in(OrderDetail::getOrderId, orderIdList)
                .eq(OrderDetail::getCompanyId, reqVo.getCompanyId())
                .eq(OrderDetail::getStoreId, reqVo.getStoreId())
                .eq(OrderDetail::getMealType, reqVo.getMealType())
                .select(OrderDetail::getId, OrderDetail::getLockerInfo, OrderDetail::getLockerBoxNo)
                // 根据柜门编号升序排列，以保证打开柜门时是按顺序的
                .orderByAsc(OrderDetail::getLockerBoxNo);
        List<OrderDetail> orderDetailList = orderDetailMapper.selectList(orderDetailQueryWrapper);
        if (CollectionUtils.isEmpty(orderDetailList)) {
            log.warn("/openAllDeliveryLockerForOrder, 没有符合条件的订单明细, companyId: {}, dateFlag: {}, mealType:{}, storeId: {}, orderIdList: {}",
                    reqVo.getCompanyId(), reqVo.getDateFlag(), reqVo.getMealType(), reqVo.getStoreId(), orderIdList);
            return ResultGenerator.genFailResult("打开柜门失败，该公司今日在该店铺没有符合条件的订单");
        }

        // 找出包含 locker_info 的订单详情信息
        List<OrderDetail> orderNeedToOpenDoorList = orderDetailList.stream()
                .filter(orderDetail -> StringUtils.hasText(orderDetail.getLockerInfo()))
                .collect(Collectors.toList());
        log.warn("/openAllDeliveryLockerForOrder, {} 个订单详情需要打开柜门", orderNeedToOpenDoorList.size());

        List<DeliveryLockerOpenDoorVO> openDoorArgsList = new ArrayList<>(orderNeedToOpenDoorList.size());
        for (OrderDetail orderDetail : orderNeedToOpenDoorList) {
            String lockerInfo = orderDetail.getLockerInfo();
            DeliverBoxApiParamVO boxApiParamVO = JSONObject.parseObject(lockerInfo, DeliverBoxApiParamVO.class);

            DeliveryLockerOpenDoorParameterVO openDoorVo = new DeliveryLockerOpenDoorParameterVO();
            // 这里直接使用订单详情 ID 作为 orderNo，每笔订单详情 ID 一定是不重复的
            String orderNo = orderDetail.getId();
            openDoorVo.setOrderNo(orderNo);
            Integer cab = boxApiParamVO.getCab();
            openDoorVo.setCab(String.valueOf(cab));
            // TODO: 验证 sn 一直为 1 是否可以正常批量打开柜门
            openDoorVo.setSn("1");
            Integer col = boxApiParamVO.getCol();
            openDoorVo.setCol(String.valueOf(col));
            openDoorVo.setPrice("0.00");
            openDoorVo.setNo(orderDetail.getLockerBoxNo());
            openDoorVo.setName("");
            openDoorVo.setType("5");

            DeliveryLockerOpenDoorVO openDoorArgs = new DeliveryLockerOpenDoorVO();
            openDoorArgs.setClientNo(boxApiParamVO.getClientNo());
            openDoorArgs.setAppId(boxApiParamVO.getAppId());
            openDoorArgs.setAppKey(boxApiParamVO.getAppKey());
            openDoorArgs.setOrderNo(orderNo);
            openDoorArgs.setParameter(openDoorVo);

            openDoorArgsList.add(openDoorArgs);
        }

        // 根据 clientNo + appId + appKey 分组，以便于连接相同外卖柜控制终端的外卖柜窗口，可以调用批量开柜门接口，减少调用接口次数和等待时间
        Map<String, List<DeliveryLockerOpenDoorVO>> openDoorArgsGroupByClient = openDoorArgsList.stream()
                .collect(Collectors.groupingBy(args -> args.getClientNo() + args.getAppId() + args.getAppKey()));
        int callOpenDoorApiCount = 0;
        for (List<DeliveryLockerOpenDoorVO> openDoorArgsListInGroup : openDoorArgsGroupByClient.values()) {
            DeliveryLockerOpenDoorVO commonArgs = openDoorArgsListInGroup.get(0);
            String clientNo = commonArgs.getClientNo();
            String appId = commonArgs.getAppId();
            String appKey = commonArgs.getAppKey();
            List<DeliveryLockerOpenDoorParameterVO> parameter = openDoorArgsListInGroup.stream()
                    .map(DeliveryLockerOpenDoorVO::getParameter)
                    .collect(Collectors.toList());

            // 调用批量打开柜门接口
            log.info("/openAllDeliveryLockerForOrder, 对于 clientNo: {}, appId: {}, appKey: {} 的外卖柜终端，有 {} 个窗口需要打开", clientNo, appId, appKey, parameter.size());
            deliveryLockerOpenApiService.openDoorListMoreThanFive(clientNo, appId, appKey, parameter, reqVo.getWaitIntervalInMs());
            callOpenDoorApiCount++;
        }
        log.info("/openAllDeliveryLockerForOrder, 成功调用打开柜门接口 {} 次", callOpenDoorApiCount);

        return ResultGenerator.genSuccessResult();
    }


    /**
     * 6.6.4 查询某个公司所属的外卖柜列表
     *
     * @param companyId 公司 ID
     */
    @GetMapping("/queryDeliveryLockerCabList")
    public Result<List<DeliveryLockerCabRespVo>> queryDeliveryLockerCabList(@RequestParam(value = "companyId") String companyId) {
        log.info("/queryDeliveryLockerCabList, companyId: {}", companyId);
        LambdaQueryWrapper<DeliveryLocker> deliveryLockerQueryWrapper = new LambdaQueryWrapper<DeliveryLocker>()
                .eq(DeliveryLocker::getCompanyId, companyId)
                .eq(DeliveryLocker::getDelFlag, false)
                // 根据货柜编码升序排列
                .orderByAsc(DeliveryLocker::getShortName);
        List<DeliveryLocker> deliveryLockers = deliverLockerMapper.selectList(deliveryLockerQueryWrapper);
        List<DeliveryLockerCabRespVo> voList = deliveryLockers.stream()
                .map(this::buildDeliveryLockerCabRespVo)
                .collect(Collectors.toList());
        log.info("/queryDeliveryLockerCabList, response: {}", JSONObject.toJSONString(voList));
        return ResultGenerator.genSuccessResult(voList);
    }

    private DeliveryLockerCabRespVo buildDeliveryLockerCabRespVo(DeliveryLocker deliveryLocker) {
        DeliveryLockerCabRespVo vo = new DeliveryLockerCabRespVo();
        vo.setDeliveryLockerId(deliveryLocker.getId());
        vo.setDeliveryLockerName(deliveryLocker.getName());
        vo.setDeliveryLockerShortName(deliveryLocker.getShortName());
        vo.setDeliveryLockerCab(deliveryLocker.getCab());
        return vo;
    }

    /**
     * 6.6.5 通过外卖柜 ID 打开外卖柜所有的窗口
     *
     * @param deliveryLockerId 外卖柜 ID
     */
    @GetMapping("/openDeliveryLockerById")
    public Result<Void> openDeliveryLockerById(@RequestParam(value = "deliveryLockerId") String deliveryLockerId,
                                               @RequestParam(value = "waitIntervalInMs", defaultValue = "3000") Integer waitIntervalInMs) {
        log.info("/openDeliveryLockerById, deliveryLockerId: {}, waitIntervalInMs: {}", deliveryLockerId, waitIntervalInMs);
        DeliveryLocker deliveryLocker = deliverLockerMapper.selectById(deliveryLockerId);
        if (deliveryLocker == null) {
            log.warn("/openDeliveryLockerById, 外卖柜不存在, deliveryLockerId: {}", deliveryLockerId);
            return ResultGenerator.genFailResult("打开失败，ID 为 " + deliveryLockerId + " 的外卖柜不存在");
        }

        String clientNo = deliveryLocker.getClientNo();
        // 使用 UUID 生成一个 32 位的字符串作为 orderNo
        String orderNo = UUID.randomUUID().toString().replace("-", "");
        String appId = deliveryLocker.getAppId();
        String appKey = deliveryLocker.getAppKey();
        Integer cab = deliveryLocker.getCab();

        int groupSize = 5;

        Integer colStart = deliveryLocker.getColStart();
        Integer colEnd = deliveryLocker.getColEnd();
        List<DeliveryLockerOpenDoorParameterVO> parameterList = new ArrayList<>();
        for (int col = colStart; col <= colEnd; col++) {
            DeliveryLockerOpenDoorParameterVO parameter = new DeliveryLockerOpenDoorParameterVO();
            // 计算这一组的开始窗口号
            int batchColStart = ((col - 1) / groupSize) * groupSize + 1;
            // 计算这一组的结束窗口号
            int batchColEnd = Math.min(batchColStart + groupSize - 1, colEnd);
            // 每五个一组，更换一次 orderNo
            if ((col - 1) % groupSize == 0) {
                orderNo = UUID.randomUUID().toString().replace("-", "");
            }
            // 打开某个柜子的所有柜门时，每次打开5个，使用同一个虚拟orderNo，orderNo格式为  shortName_label_startCol_endCol ，orderNo 编码了 柜子简称，柜子 贴标，本次开柜门接口调用，打开 那几个窗格
            String messageForOrderNo = MessageFormat.format("{0}_{1}_{2}_{3}", deliveryLocker.getShortName(), deliveryLocker.getLockerLabel(), batchColStart, batchColEnd);
            parameter.setOrderNo(orderNo);
            parameter.setCab(String.valueOf(cab));
            String sn = String.valueOf(col % 5);
            parameter.setSn(sn);
            parameter.setCol(String.valueOf(col));
            parameter.setPrice("0.00");
            parameter.setNo(MessageFormat.format("{0}_{1}", deliveryLocker.getLockerLabel(), col));
            parameter.setName("");
            parameter.setType("5");
            parameterList.add(parameter);
            log.info("/openDeliveryLockerById, orderNo: {}, messageForOrderNo: {}, cab: {}, col: {}, sn: {}", orderNo, messageForOrderNo, cab, col, sn);
        }

        deliveryLockerOpenApiService.openDoorListMoreThanFive(clientNo, appId, appKey, parameterList, waitIntervalInMs);
        return ResultGenerator.genSuccessResult();
    }


    /**
     * 6.6.6 获取货柜编号及窗户编号下拉列表
     *
     * @param companyId 公司 ID
     */
    @GetMapping("/queryDeliveryCabAndBoxInfoList")
    public Result<List<DeliveryLockerCabAndBoxInfoRespVo>> queryDeliveryCabAndBoxInfoList(@RequestParam(value = "companyId") String companyId) {
        log.info("/queryDeliveryCabAndBoxInfoList, companyId: {}", companyId);
        LambdaQueryWrapper<DeliveryLocker> deliveryLockerQueryWrapper = new LambdaQueryWrapper<DeliveryLocker>()
                .eq(DeliveryLocker::getCompanyId, companyId)
                .eq(DeliveryLocker::getDelFlag, false)
                // 根据货柜编码升序排列
                .orderByAsc(DeliveryLocker::getShortName);
        List<DeliveryLocker> deliveryLockerList = deliverLockerMapper.selectList(deliveryLockerQueryWrapper);
        List<DeliveryLockerCabAndBoxInfoRespVo> voList = deliveryLockerList.stream()
                .map(this::buildDeliveryLockerCabAndBoxInfoRespVo)
                .collect(Collectors.toList());
        log.info("/queryDeliveryCabAndBoxInfoList, response: {}", JSONObject.toJSONString(voList));
        return ResultGenerator.genSuccessResult(voList);
    }

    private DeliveryLockerCabAndBoxInfoRespVo buildDeliveryLockerCabAndBoxInfoRespVo(DeliveryLocker deliveryLocker) {
        DeliveryLockerCabAndBoxInfoRespVo vo = new DeliveryLockerCabAndBoxInfoRespVo();
        vo.setDeliveryLockerId(deliveryLocker.getId());
        vo.setDeliveryLockerName(deliveryLocker.getName());
        vo.setDeliveryLockerShortName(deliveryLocker.getShortName());
        vo.setDeliveryLockerCab(deliveryLocker.getCab());

        Integer colStart = deliveryLocker.getColStart();
        Integer colEnd = deliveryLocker.getColEnd();
        vo.setDeliveryLockerColStart(colStart);
        vo.setDeliveryLockerColEnd(colEnd);

        List<Integer> colList = new ArrayList<>(colEnd - colStart + 1);
        for (int i = colStart; i <= deliveryLocker.getColEnd(); i++) {
            colList.add(i);
        }
        vo.setDeliveryLockerColList(colList);
        return vo;
    }


    /**
     * 6.6.7 查询批量打开柜门时默认的等待间隔时间
     *
     * @param companyId 公司 ID
     */
    @GetMapping("/queryWaitIntervalForBatchOpenMultiDoor")
    public Result<List<WaitIntervalForBatchOpenMultiDoorRespVo>> queryWaitIntervalForBatchOpenMultiDoor(@RequestParam(value = "companyId", required = false, defaultValue = "00000000000000000000000000000000") String companyId) {
        log.info("/queryWaitIntervalForBatchOpenMultiDoor, companyId: {}", companyId);
        LambdaQueryWrapper<DeliveryLocker> deliveryLockerQueryWrapper = new LambdaQueryWrapper<DeliveryLocker>()
                .eq(DeliveryLocker::getCompanyId, companyId)
                .eq(DeliveryLocker::getDelFlag, false)
                // 根据调用开柜门接口的等待间隔时间降序排序
                .orderByDesc(DeliveryLocker::getWaitInterval);
        List<DeliveryLocker> deliveryLockerList = deliverLockerMapper.selectList(deliveryLockerQueryWrapper);
        int waitInternalInMs = defaultSelectedWaitIntervalInMs;
        if (!CollectionUtils.isEmpty(deliveryLockerList)) {
            // 由于返回的外卖柜信息按照等待间隔时间降序排列，所以返回最长的等待间隔时间时，返回第一个元素的等待间隔时间即可
            waitInternalInMs = deliveryLockerList.get(0).getWaitInterval();
        }

        // 根据配置项构建默认的等待间隔时间下拉列表
        List<WaitIntervalForBatchOpenMultiDoorRespVo> voList = Arrays.stream(waitIntervalInMsList.split(","))
                .map(Integer::valueOf)
                .map(waitInterval -> new WaitIntervalForBatchOpenMultiDoorRespVo(waitInterval, false))
                .collect(Collectors.toList());

        // 设置选中项
        for (WaitIntervalForBatchOpenMultiDoorRespVo vo : voList) {
            if (vo.getWaitIntervalInMs().equals(waitInternalInMs)) {
                vo.setSelected(true);
            }
        }

        log.info("/queryWaitIntervalForBatchOpenMultiDoor, response: {}", JSONObject.toJSONString(voList));
        return ResultGenerator.genSuccessResult(voList);
    }
}
